"""
将装饰器定义为类

问题：
    你想使用一个装饰器去包装函数，但是希望返回一个可调用的实例。
    你需要让你的装饰器可以同时工作在类定义的内部和外部。
解决方案：
    为了将装饰器定义成一个实例，你需要确保它实现了 __call__() 和 __get__() 方法。
"""

import types
from functools import wraps


class User:
    def __init__(self, func):
        wraps(func)(self)
        self.ncalls = 0

    def __call__(self, *args, **kwargs):
        self.ncalls += 1
        return self.__wrapped__(*args, **kwargs)

    def __get__(self, instance, owner):
        if instance is None:
            return self
        else:
            return types.MethodType(self, instance)


@User
def add(x, y):
    return x + y


class Student:
    @User
    def say(self, content):
        print(self, content)


def use_decorator():
    print(add(1, 1))
    print(add(1, 2))
    print(add.ncalls)
    # 输出结果：
    """
    2
    3
    2
    """

    student = Student()
    print(student.say("hi kevin"))
    print(student.say.ncalls)
    # 输出结果：
    """
    <__main__.Student object at 0x10d9ec978> hi kevin
    None
    1
    """


if __name__ == '__main__':
    use_decorator()
